#include "widget.h"
#include "ui_widget.h"
#include "iostream"
#include <QOpenGLShader>
#include <QVector4D>
#include <QtMath>
#include <QImage>

using namespace std;

Widget::Widget(QWidget *parent)
    : QOpenGLWidget(parent)
    //, ui(new Ui::Widget)
{
    //ui->setupUi(this);
}

Widget::~Widget()
{
    //delete ui;
    delete shaderColor;
    delete shaderLight;
    delete camera;
    delete texture1;
    delete texture2;
}


void Widget::initializeGL() {
    cout<<"initializeGL"<<endl;
    float vertices[] = {
            // positions          // normals           // texture coords
            -0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f,  0.0f,  0.0f,
             0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f,  1.0f,  0.0f,
             0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f,  1.0f,  1.0f,
             0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f,  1.0f,  1.0f,
            -0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f,  0.0f,  1.0f,
            -0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f,  0.0f,  0.0f,

            -0.5f, -0.5f,  0.5f,  0.0f,  0.0f,  1.0f,  0.0f,  0.0f,
             0.5f, -0.5f,  0.5f,  0.0f,  0.0f,  1.0f,  1.0f,  0.0f,
             0.5f,  0.5f,  0.5f,  0.0f,  0.0f,  1.0f,  1.0f,  1.0f,
             0.5f,  0.5f,  0.5f,  0.0f,  0.0f,  1.0f,  1.0f,  1.0f,
            -0.5f,  0.5f,  0.5f,  0.0f,  0.0f,  1.0f,  0.0f,  1.0f,
            -0.5f, -0.5f,  0.5f,  0.0f,  0.0f,  1.0f,  0.0f,  0.0f,

            -0.5f,  0.5f,  0.5f, -1.0f,  0.0f,  0.0f,  1.0f,  0.0f,
            -0.5f,  0.5f, -0.5f, -1.0f,  0.0f,  0.0f,  1.0f,  1.0f,
            -0.5f, -0.5f, -0.5f, -1.0f,  0.0f,  0.0f,  0.0f,  1.0f,
            -0.5f, -0.5f, -0.5f, -1.0f,  0.0f,  0.0f,  0.0f,  1.0f,
            -0.5f, -0.5f,  0.5f, -1.0f,  0.0f,  0.0f,  0.0f,  0.0f,
            -0.5f,  0.5f,  0.5f, -1.0f,  0.0f,  0.0f,  1.0f,  0.0f,

             0.5f,  0.5f,  0.5f,  1.0f,  0.0f,  0.0f,  1.0f,  0.0f,
             0.5f,  0.5f, -0.5f,  1.0f,  0.0f,  0.0f,  1.0f,  1.0f,
             0.5f, -0.5f, -0.5f,  1.0f,  0.0f,  0.0f,  0.0f,  1.0f,
             0.5f, -0.5f, -0.5f,  1.0f,  0.0f,  0.0f,  0.0f,  1.0f,
             0.5f, -0.5f,  0.5f,  1.0f,  0.0f,  0.0f,  0.0f,  0.0f,
             0.5f,  0.5f,  0.5f,  1.0f,  0.0f,  0.0f,  1.0f,  0.0f,

            -0.5f, -0.5f, -0.5f,  0.0f, -1.0f,  0.0f,  0.0f,  1.0f,
             0.5f, -0.5f, -0.5f,  0.0f, -1.0f,  0.0f,  1.0f,  1.0f,
             0.5f, -0.5f,  0.5f,  0.0f, -1.0f,  0.0f,  1.0f,  0.0f,
             0.5f, -0.5f,  0.5f,  0.0f, -1.0f,  0.0f,  1.0f,  0.0f,
            -0.5f, -0.5f,  0.5f,  0.0f, -1.0f,  0.0f,  0.0f,  0.0f,
            -0.5f, -0.5f, -0.5f,  0.0f, -1.0f,  0.0f,  0.0f,  1.0f,

            -0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f,  0.0f,  1.0f,
             0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f,  1.0f,  1.0f,
             0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,  1.0f,  0.0f,
             0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,  1.0f,  0.0f,
            -0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,  0.0f,  0.0f,
            -0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f,  0.0f,  1.0f
        };


    core = QOpenGLContext::currentContext()->versionFunctions<QOpenGLFunctions_3_3_Core>();

    core->glGenVertexArrays(1, &VAO);

    camera = new Camera(QVector3D(0,0,3), width(), height());
    // 所有跟gl_array_buffer有关系的操作状态，它都记录下来

    // 创造vbo
    core->glGenBuffers(1, &VBO);
    core->glGenBuffers(1, &EBO);

    core->glBindVertexArray(VAO); // 开始录制

    core->glBindBuffer(GL_ARRAY_BUFFER, VBO);

    core->glBufferData(GL_ARRAY_BUFFER, sizeof (vertices), vertices, GL_STATIC_DRAW);
    auto step=8*sizeof (GLfloat);
    core->glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, step, (void*)0);
    // 1. location 用来跟着色器对应的，一般大小为16个
    // 2. 顶点属性的大小
    // 3. 类型
    // 4. 是否标准化
    // 5. 步长

    core->glEnableVertexAttribArray(0);


    core->glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, step, (void*)(3*sizeof(GLfloat)));
    core->glEnableVertexAttribArray(1);


    core->glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, step, (void*)(6*sizeof(GLfloat)));
    core->glEnableVertexAttribArray(2);

    // 绑定空缓冲区
    core->glBindVertexArray(0);
    core->glBindBuffer(GL_ARRAY_BUFFER, 0);

    texture1 = new QOpenGLTexture(QImage("D:/selfad/alg_and_graph/qt-opengl/images/container2.png"));
    texture1->setWrapMode(QOpenGLTexture::Repeat);
    texture1->setMinificationFilter(QOpenGLTexture::Nearest);
    texture1->setMagnificationFilter(QOpenGLTexture::Linear);

    texture2 = new QOpenGLTexture(QImage("D:/selfad/alg_and_graph/qt-opengl/images/container2_specular.png"));
    texture2->setWrapMode(QOpenGLTexture::Repeat);
    texture2->setMinificationFilter(QOpenGLTexture::Nearest);
    texture2->setMagnificationFilter(QOpenGLTexture::Linear);

    // 着色器
    shaderColor = new Shader(":/shaders/shader/box.vert", ":/shaders/shader/box.frag");
    shaderColor->setInt("material.diffuse", 0);
    shaderColor->setInt("material.specular", 1);

    shaderLight = new Shader(":/shaders/shader/light.vert", ":/shaders/shader/light.frag");
}
void Widget::paintGL()
{
   // cout<<"paint GL"<<endl;
    core->glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
    core->glClear(GL_COLOR_BUFFER_BIT);
    core->glEnable(GL_DEPTH_TEST);
    camera->DealKeyboardInputs();

    core->glActiveTexture(GL_TEXTURE0);
    texture1->bind();
    core->glActiveTexture(GL_TEXTURE1);
    texture2->bind();
    // 绘制箱子
    shaderColor->use();    
    shaderColor->setVec3("light.direction", {-0.2f, -1.0f, -0.3f});
    shaderColor->setVec3("viewPos", camera->cameraPos);
    shaderColor->setVec3("material.specular", {0.5f, 0.5f, 0.5f});
    shaderColor->setFloat("material.shininess", 64.0f);

    shaderColor->setVec3("light.ambient", { 0.2f, 0.2f, 0.2f});
    shaderColor->setVec3("light.diffuse", {0.5f, 0.5f, 0.5f}); // 将光照调暗了一些以搭配场景
    shaderColor->setVec3("light.specular", {1.0f, 1.0f, 1.0f});
    //qDebug()<< camera->cameraPos<<endl;


       shaderColor->setMat4("view", camera->getViewMat());
    QMatrix4x4 projection;
    //projection.setToIdentity();
    projection.perspective(camera->zoom, GLfloat(this->width())/GLfloat(this->height()), 0.1f, 100);
    shaderColor->setMat4("projection", projection);
    core->glBindVertexArray(VAO); // 执行数据传递过程

//    QMatrix4x4 model;
//       model.setToIdentity();

//       shaderColor->setMat4("model", model);
//    core->glDrawArrays(GL_TRIANGLES, 0, 36);
    QVector3D cubePositions[] = {
           { 0.0f,  0.0f,  0.0f},
           { 2.0f,  5.0f, -15.0f},
         {-1.5f, -2.2f, -2.5f},
         {-3.8f, -2.0f, -12.3f},
         { 2.4f, -0.4f, -3.5f},
         {-1.7f,  3.0f, -7.5f},
         { 1.3f, -2.0f, -2.5f},
         { 1.5f,  2.0f, -2.5f},
         { 1.5f,  0.2f, -1.5f},
         {-1.3f,  1.0f, -1.5f}
       };
    for(int i=0;i<10;++i) {
        QMatrix4x4 model;
        model.translate(cubePositions[i]);
        float angle = 20.0f * i;
        model.rotate(angle, {1.0f, 0.3f, 0.5f});
        shaderColor->setMat4("model", model);
        core->glDrawArrays(GL_TRIANGLES, 0, 36);
    }

    core->glBindVertexArray(0);

    update();
}
void Widget::resizeGL(int w, int h){
    cout<<"w : "<< w<< "h : "<<h<< endl;
}

void Widget::keyPressEvent(QKeyEvent *event) {
    cout<<"key: down" <<endl;
    camera->keys[event->key()]=GL_TRUE;
}

void Widget::keyReleaseEvent(QKeyEvent *event) {
    cout<<"key: up" <<endl;
    if(event->key()<0 || event->key()>=1024)return;
    camera->keys[event->key()]=GL_FALSE;
}


void Widget::mousePressEvent(QMouseEvent *event){
    cout<<"mouse: down" <<endl;
    if(event->button() == Qt::LeftButton) camera->isLeftMousePress = GL_TRUE;
}
void Widget::mouseReleaseEvent(QMouseEvent *event){
    cout<<"mouse: release" <<endl;
    if(event->button()==Qt::LeftButton) {
        camera->isLeftMousePress = GL_FALSE;
        camera->isFirstMouse = GL_TRUE;
    }
}
void Widget::mouseMoveEvent(QMouseEvent *event){
    cout<<"mouse: move" <<endl;
    GLint xpos = event->pos().x();
    GLint ypos = event->pos().y();

    if(camera->isLeftMousePress) {
        if(camera->isFirstMouse) {
            camera->lastX=xpos;
            camera->lastY = ypos;
            camera->isFirstMouse = GL_FALSE;
        }

        GLint Xoffset = xpos - camera->lastX;
        GLint Yoffset = camera->lastY - ypos;
        camera->lastX = xpos;
        camera->lastY = ypos;

        camera->DealMouseMovement(Xoffset, Yoffset);
    }
}
void Widget::wheelEvent(QWheelEvent *event){
    cout<<"wheel: roll" <<endl;
    QPoint offset = event->angleDelta();
    camera->DealMouseScroll(offset.y()/20.0f);
}
